package ServiceLayer;

import java.io.File;
import java.net.URL;
import java.util.Hashtable;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;

public class XMLRoomParser {
	/* Variables necessary for loading the .xml file and for parsing. */
	private DocumentBuilderFactory dbFactory;
	private DocumentBuilder dBuilder;
	private Document room_Data;

	private String[] tileSetURLs; 			// holds location of tile sets
	private Bitmap[] tileSets; 	// holds the data of tile sets

	private int tileSetNum; 		// Number of tile sets used
	private int[] tileSetFirstIds; 	// the first ids used by the tile sets (e.g. set1 ends @ 16, set2 begins @ 17)
	private int tileWidth; 			// width of tiles
	private int tileHeight; 		// height of tiles

	private int layerNum; 			// # of layers used
	private int collisionLayer; 	// value of the collisionLayer
	private int collisionFirstId; 	// the first id of the collision tileset
	private int playerLayer; 		// the value of the playerLayer
	private int interactionLayer;	// the value of the interaction layer
	private int interactionFirstId; // the first id of the interaction layer
	private int room_Width; 		// the width of the map in # of tiles
	private int room_Height;		// and the height of the room in # of tiles

	/*
	 * This is the key variable generated by this class. It is a three
	 * dimensional array that contains all of the ids of the tiles in a given
	 * (z,y,x) coordinate. The format is [layer][yCoordinate][xCoordinate].
	 * Example: if layeredMap[1][5][9] = 3, then the tile with the id "3" is located at
	 * the point (8, 4) on the second layer.
	 */
	private int[][][] layeredMap;
	private int[][] interactionMap;

	/* Constructor that sets all of the information parsed from the .xml file. */
	public XMLRoomParser(String roomURL, Context context) {
		try {
			System.out.println("roomURL = " + roomURL);
			AssetManager assetManager = context.getAssets();

			dbFactory = DocumentBuilderFactory.newInstance();
			dBuilder = dbFactory.newDocumentBuilder();
			room_Data = dBuilder.parse(assetManager.open(roomURL));
			room_Data.getDocumentElement().normalize();

			NodeList mapAttrList = room_Data.getElementsByTagName("map");
			Element mapAttr = (Element) mapAttrList.item(0);
			room_Width = Integer.parseInt(mapAttr.getAttribute("width"));
			room_Height = Integer.parseInt(mapAttr.getAttribute("height"));
			tileWidth = Integer.parseInt(mapAttr.getAttribute("tilewidth"));
			tileHeight = Integer.parseInt(mapAttr.getAttribute("tileheight"));

			NodeList tileSetList = room_Data.getElementsByTagName("image");
			NodeList tileSetAttr = room_Data.getElementsByTagName("tileset");
			tileSetNum = tileSetAttr.getLength();
			tileSetURLs = new String[tileSetNum];
			tileSets = new Bitmap[tileSetNum];
			tileSetFirstIds = new int[tileSetNum];

			String sourceAttribute = "";
			String nameAttribute = "";
			for (int i = 0; i < tileSetNum; i++) {
				Element image = (Element) tileSetList.item(i);
				Element tileSet = (Element) tileSetAttr.item(i);
				nameAttribute = tileSet.getAttribute("name").toLowerCase();
				sourceAttribute = image.getAttribute("source");
				
				// ignore the dots from the tileset's FilePaths
				if (sourceAttribute.startsWith(".."))
					sourceAttribute = sourceAttribute.substring(2);
				if (sourceAttribute.startsWith("/"))
					sourceAttribute = sourceAttribute.substring(1);
				tileSetURLs[i] = sourceAttribute;
				tileSetFirstIds[i] = Integer.parseInt(tileSet
						.getAttribute("firstgid")) - 1;

				// ------------- collision layer
				if (nameAttribute.startsWith("col")) {
					collisionFirstId = tileSetFirstIds[i];
				} 
				// ------------- interaction layer
				else if(nameAttribute.startsWith("int")) { 
					interactionFirstId = tileSetFirstIds[i];
				}
				try {
					tileSets[i] = BitmapFactory.decodeStream(context.getAssets().open(tileSetURLs[i]));;
				} catch (Exception e) {
					e.printStackTrace();
				}
			}

			NodeList layerList = room_Data.getElementsByTagName("layer");
			layerNum = layerList.getLength();
			
			for (int i = 0; i < layerNum; i++) {
				Element layerSource = (Element) layerList.item(i);
				nameAttribute = layerSource.getAttribute("name").toLowerCase();
				// ------------- collision layer
				if (nameAttribute.startsWith("col")) {
					collisionLayer = i;
				}
				// ------------- interaction layer
				else if(nameAttribute.startsWith("int")) {
					interactionLayer = i;
				}
				// ------------- player layer
				else if (nameAttribute.startsWith("play")) {
					playerLayer = i;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		formMap();
	}

	private Hashtable<Point, String> targets = new Hashtable<Point, String>();
	
	public Hashtable<Point, String> getTargets() {
		return targets;
	}

	/* Forms the layeredMap[][][] array. */
	private void formMap() {
		layeredMap = new int[layerNum][room_Width][room_Height];
		interactionMap = new int[room_Width][room_Height];
		String target = "";
		int value=-8000;
		Point point;

		NodeList tileList = room_Data.getElementsByTagName("tile");
		int count = 0;
		for (int z = 0; z < layerNum; z++) {
			if (z != playerLayer) {
				for (int y = 0; y < room_Height; y++) {
					for (int x = 0; x < room_Width; x++) {
						Element tile = (Element) tileList.item(count);
						if (z != collisionLayer & z!= interactionLayer){
							// sets tile id's (-1 if it's an empty tile in that layer)
							layeredMap[z][y][x] = Integer.parseInt(tile.getAttribute("gid")) - 1;
						} 
						else if (z == collisionLayer) {
							layeredMap[z][y][x] = Integer.parseInt(tile.getAttribute("gid"))- 1- collisionFirstId;
						}
						else if (z == interactionLayer){
							value = Integer.parseInt(tile.getAttribute("gid"))- 1- interactionFirstId;
							layeredMap[z][y][x] = value;
							interactionMap[y][x] = value;
							if(tile.hasAttribute("target")){
								point = new Point(x, y);
								targets.put(point, tile.getAttribute("target")); 
							}
						}
						count++;
					}
				}
			}
		}
	}

	/* Getters for the various relevant layeredMap info variables. */
	public int[][] getRoomMap() {
		return interactionMap;
	}

	public int[][][] getLayeredMap() {
		return layeredMap;
	}

	public Bitmap[] getTileSets() {
		return tileSets;
	}

	public int getRoomWidth() {
		return room_Width;
	}

	public int getRoomHeight() {
		return room_Height;
	}

	public int getLayerNum() {
		return layerNum;
	}

	public int getTileWidth() {
		return tileWidth;
	}

	public int getTileHeight() {
		return tileHeight;
	}

	public int[] getTileSetFirstIds() {
		return tileSetFirstIds;
	}

	public int getCollisionLayer() {
		return collisionLayer;
	}

	public int getPlayerLayer() {
		return playerLayer;
	}

	public int getInteractionLayer() {
		return interactionLayer;
	}
}
